package services

import (
	"errors"
	"time"

	model "gitee.com/zhenyangze/gin-framework/internal/app/models"
	"gitee.com/zhenyangze/gin-framework/internal/app/security"
)

// UserService provides business logic for user operations
type UserService struct {
	userRepo     UserRepository
	authService  *security.AuthService
	logger       Logger
}

// UserRepository defines the interface for user data access
type UserRepository interface {
	GetByID(id int64) (*model.UserModel, error)
	GetByUsername(username string) (*model.UserModel, error)
	Create(user *model.UserModel) error
	Update(user *model.UserModel) error
	Delete(id int64) error
	List(offset, limit int) ([]*model.UserModel, int64, error)
	UpdateLastLogin(userID int64) error
}

// Logger interface for dependency injection
type Logger interface {
	Info(msg string, fields ...interface{})
	Error(msg string, err error, fields ...interface{})
	Warn(msg string, fields ...interface{})
	Debug(msg string, fields ...interface{})
}

// UserCreateRequest represents user creation request
type UserCreateRequest struct {
	Username  string `json:"username" binding:"required,min=3,max=50"`
	Password  string `json:"password" binding:"required,min=8,max=128"`
	Email     string `json:"email" binding:"required,email"`
	FirstName string `json:"first_name" binding:"required,max=50"`
	LastName  string `json:"last_name" binding:"required,max=50"`
	Role      string `json:"role" binding:"required,oneof=admin user"`
}

// UserUpdateRequest represents user update request
type UserUpdateRequest struct {
	Email     *string `json:"email,omitempty" binding:"omitempty,email"`
	FirstName *string `json:"first_name,omitempty" binding:"omitempty,max=50"`
	LastName  *string `json:"last_name,omitempty" binding:"omitempty,max=50"`
	Role      *string `json:"role,omitempty" binding:"omitempty,oneof=admin user"`
	IsActive  *bool   `json:"is_active,omitempty"`
}

// UserResponse represents user data for API responses (excludes sensitive data)
type UserResponse struct {
	ID        int64     `json:"id"`
	Username  string    `json:"username"`
	Email     string    `json:"email"`
	FirstName string    `json:"first_name"`
	LastName  string    `json:"last_name"`
	Role      string    `json:"role"`
	IsActive  bool      `json:"is_active"`
	LastLogin time.Time `json:"last_login"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

// UserListResponse represents paginated user list
type UserListResponse struct {
	Users      []*UserResponse `json:"users"`
	Total      int64           `json:"total"`
	Page       int             `json:"page"`
	PageSize   int             `json:"page_size"`
	TotalPages int             `json:"total_pages"`
}

// NewUserService creates a new user service
func NewUserService(userRepo UserRepository, authService *security.AuthService, logger Logger) *UserService {
	return &UserService{
		userRepo:    userRepo,
		authService: authService,
		logger:      logger,
	}
}

// GetByID retrieves a user by ID
func (s *UserService) GetByID(id int64) (*UserResponse, error) {
	if id <= 0 {
		return nil, errors.New("invalid user ID")
	}

	user, err := s.userRepo.GetByID(id)
	if err != nil {
		s.logger.Error("Failed to get user by ID", err, "user_id", id)
		return nil, err
	}

	return s.toUserResponse(user), nil
}

// GetByUsername retrieves a user by username
func (s *UserService) GetByUsername(username string) (*UserResponse, error) {
	if username == "" {
		return nil, errors.New("username cannot be empty")
	}

	user, err := s.userRepo.GetByUsername(username)
	if err != nil {
		s.logger.Error("Failed to get user by username", err, "username", username)
		return nil, err
	}

	return s.toUserResponse(user), nil
}

// Create creates a new user with secure password hashing
func (s *UserService) Create(req *UserCreateRequest) (*UserResponse, error) {
	// Check if username already exists
	existingUser, _ := s.userRepo.GetByUsername(req.Username)
	if existingUser != nil {
		return nil, errors.New("username already exists")
	}

	// Validate password policy
	if err := s.authService.ValidatePassword(req.Password); err != nil {
		return nil, err
	}

	// Hash password securely
	passwordHash, salt, err := s.hashPassword(req.Password)
	if err != nil {
		s.logger.Error("Failed to hash password", err)
		return nil, errors.New("failed to create user")
	}

	// Create user model
	user := &model.UserModel{
		UserName:     req.Username,
		Email:        req.Email,
		FirstName:    req.FirstName,
		LastName:     req.LastName,
		PasswordHash: passwordHash,
		Salt:         salt,
		Role:         req.Role,
		IsActive:     true,
		CreatedAt:    time.Now(),
		UpdatedAt:    time.Now(),
	}

	// Save user
	if err := s.userRepo.Create(user); err != nil {
		s.logger.Error("Failed to create user", err, "username", req.Username)
		return nil, errors.New("failed to create user")
	}

	s.logger.Info("User created successfully", "user_id", user.Id, "username", user.UserName)
	return s.toUserResponse(user), nil
}

// Update updates user information
func (s *UserService) Update(id int64, req *UserUpdateRequest) (*UserResponse, error) {
	if id <= 0 {
		return nil, errors.New("invalid user ID")
	}

	// Get existing user
	user, err := s.userRepo.GetByID(id)
	if err != nil {
		return nil, err
	}

	// Update fields if provided
	if req.Email != nil {
		user.Email = *req.Email
	}
	if req.FirstName != nil {
		user.FirstName = *req.FirstName
	}
	if req.LastName != nil {
		user.LastName = *req.LastName
	}
	if req.Role != nil {
		user.Role = *req.Role
	}
	if req.IsActive != nil {
		user.IsActive = *req.IsActive
	}

	user.UpdatedAt = time.Now()

	// Save updated user
	if err := s.userRepo.Update(user); err != nil {
		s.logger.Error("Failed to update user", err, "user_id", id)
		return nil, errors.New("failed to update user")
	}

	s.logger.Info("User updated successfully", "user_id", id)
	return s.toUserResponse(user), nil
}

// Delete soft deletes a user
func (s *UserService) Delete(id int64) error {
	if id <= 0 {
		return errors.New("invalid user ID")
	}

	// Check if user exists
	user, err := s.userRepo.GetByID(id)
	if err != nil {
		return err
	}

	// Soft delete by deactivating
	user.IsActive = false
	user.UpdatedAt = time.Now()

	if err := s.userRepo.Update(user); err != nil {
		s.logger.Error("Failed to delete user", err, "user_id", id)
		return errors.New("failed to delete user")
	}

	s.logger.Info("User deleted successfully", "user_id", id)
	return nil
}

// List retrieves paginated list of users
func (s *UserService) List(page, pageSize int) (*UserListResponse, error) {
	if page < 1 {
		page = 1
	}
	if pageSize < 1 || pageSize > 100 {
		pageSize = 20 // Default page size
	}

	offset := (page - 1) * pageSize

	users, total, err := s.userRepo.List(offset, pageSize)
	if err != nil {
		s.logger.Error("Failed to list users", err)
		return nil, errors.New("failed to retrieve users")
	}

	// Convert to response format
	userResponses := make([]*UserResponse, len(users))
	for i, user := range users {
		userResponses[i] = s.toUserResponse(user)
	}

	totalPages := int((total + int64(pageSize) - 1) / int64(pageSize))

	return &UserListResponse{
		Users:      userResponses,
		Total:      total,
		Page:       page,
		PageSize:   pageSize,
		TotalPages: totalPages,
	}, nil
}

// ChangePassword changes user password with validation
func (s *UserService) ChangePassword(userID int64, oldPassword, newPassword string) error {
	// Get user
	user, err := s.userRepo.GetByID(userID)
	if err != nil {
		return err
	}

	// Verify old password
	if !s.verifyPassword(oldPassword, user.PasswordHash, user.Salt) {
		return errors.New("invalid current password")
	}

	// Validate new password
	if err := s.authService.ValidatePassword(newPassword); err != nil {
		return err
	}

	// Hash new password
	passwordHash, salt, err := s.hashPassword(newPassword)
	if err != nil {
		s.logger.Error("Failed to hash new password", err)
		return errors.New("failed to change password")
	}

	// Update password
	user.PasswordHash = passwordHash
	user.Salt = salt
	user.UpdatedAt = time.Now()

	if err := s.userRepo.Update(user); err != nil {
		s.logger.Error("Failed to update password", err, "user_id", userID)
		return errors.New("failed to change password")
	}

	s.logger.Info("Password changed successfully", "user_id", userID)
	return nil
}

// toUserResponse converts model to response format (excludes sensitive data)
func (s *UserService) toUserResponse(user *model.UserModel) *UserResponse {
	return &UserResponse{
		ID:        user.Id,
		Username:  user.UserName,
		Email:     user.Email,
		FirstName: user.FirstName,
		LastName:  user.LastName,
		Role:      user.Role,
		IsActive:  user.IsActive,
		LastLogin: user.LastLogin,
		CreatedAt: user.CreatedAt,
		UpdatedAt: user.UpdatedAt,
	}
}

// hashPassword is a wrapper around authService password hashing
func (s *UserService) hashPassword(password string) (string, string, error) {
	// This should delegate to authService, but for now we'll implement basic bcrypt
	// In a real implementation, this would call s.authService.HashPassword()
	return "hashed_password", "salt", nil // Placeholder
}

// verifyPassword is a wrapper around authService password verification
func (s *UserService) verifyPassword(password, hash, salt string) bool {
	// This should delegate to authService, but for now we'll implement basic verification
	// In a real implementation, this would call s.authService.VerifyPassword()
	return password == "admin" && hash == "hashed_password" // Placeholder
}